$$.sql = new function(){
	// 数据库连接对象，只设置一次，无须重复创建，多次创建则浪费。
	var connectObj;	
		
	function connectDB(cfg){
		var 
		 dbType			// 链接数据库的类型，是 Access 呢？MySQL 呢？还是 SQLServer？
		,dbPath 		// 数据库文件的路径，如果通过磁盘访问的话。
		,connectStr		// 链接数据库的字符串。
						// 数据库对象，同前面的 var connectObj; 的对象
		,connectObj = new ActiveXObject("ADODB.CONNECTION"); 
		
		if(cfg){
			dbType = cfg.dbType;
		}else if(!cfg && $$.cfg){
			dbType = $$.cfg.edk_dbType.toLowerCase();
		}else{
			dbType = 'access';	// 默认为MS Access数据库
		}
		
		switch(dbType){
			case 'access' :
				if(cfg && cfg.dbPath){
					dbPath = cfg.dbPath;
				}else if(!cfg && $$.cfg){
				    dbPath = $$.cfg.edk_isDebugging ? $$.cfg.edk_dbFilePath_Test : Server.mappath($$.cfg.edk_dbFilePath);
				}
		        connectStr = "DBQ={0};DefaultDir=;DRIVER={Microsoft Access Driver (*.mdb)};".format(dbPath);
			break;
			case 'sqlservver' :
				throw '尚未实现';
			case 'mysql' :
				// 使用driver模式不用有空格在连接字符串中
				connectStr = "DRIVER={mysql odbc 5.1 driver};SERVER={0};PORT={1};UID={2};PASSWORD={3};DATABASE={4};OPTION=3";
				connectStr = connectStr.format('localhost', 3306, 'root' ,'123', 'test');            		
			break;
			case 'sqlite' :
				dbPath = 'd:\\test.db';
				connectStr = "DRIVER={SQLite3 ODBC Driver};Database=" + dbPath;
			break;
			default:
				throw '非法数据库连接类型！';
		};

		connectObj.open(connectStr);
		
		return connectObj;
	}

	/**
     * 首先通过调用的父类方法检测 SQL 语句是否安全的。
     * 其次，该方法执行以下的步骤：
     * 一、连接数据库；二、执行 SQL 操作。三、返回数据集合。
     * 其中连接了的数据库应该对象不应立刻销毁，应予保留，
     * 使得在当前线程的上下文（整个Edk Context）内，对象 connectObj 可供以后程序多次反复地使用。
     * 注意谨记：在程序最后阶段关闭数据库连接，即 $$.sql.close();
	 * @param 	{String} 			sql 要执行的 SQL 语句（可选的）。
	 * @param   {Objcec}			cfg 配置对象，可以包含连接数据库的字符串（可选的）
	 * @return 	{ADODB.Recordset} 		数据库记录集合。
	 */
	this.execute = function (sql, cfg, isRS){
		var rs;
		
		if(!connectObj){
			connectObj = $$.sql.connectObj = connectDB(cfg); // call only once.
		}
		
		if(sql){
			rs = connectObj.execute(sql);
			if(isRS){
				return rs;			
			}else{
				return rs.EOF || rs.BOF ? -1 : row2json(rs);
			}
		}
	}
		    
    /**
     * 关闭数据库连接，释放占用的资源。
     */
    this.close = function(){
    	if( connectObj){
			connectObj.close();
    	}
    }
	
	/**
	 * 转换值的 ADO 类型为 JavaScript 类型。
	 * 更多常量信息请查阅文件：adovbs.inc ，该文件在系统中默认的路径为: C:\Program Files\Common Files\System\ado\adovbs.inc
	 * 其他系统请自行搜索。
	 * @param 	{Number} 	adoField
	 * @param 	{Any} 		value
	 * @return 	{Any}
	 */
	function sql2json(value, dataType){
		switch(dataType){
			case "string":
			case 129:		// 'char':
			case 130: 		// 'nchar':
			case 200:		// 'varchar':
			case 201:		// 'text':
			case 202:		// 'nvarchar':
			case 203:		// 'ntext':
				return String(value);
			case "number":
			case 20: 		// 'bigint':
			case 2:			// 'smallint':
			case 16: 		// 'tinyint':
			case 14: 		// 'decimal':
			case 5:			// 'double':
			case 3:
				return Number(value);
			case 11: 		// "boolean":
			case 'bol':
				return Boolean(value);
			case "date":
			case "object":
			case 135:		// 'datetime':
			case 64:		// 'fileTime':
			case 134:
			case 137:
				return new Date(value).format("yyyy-mm-dd");
			default:
				return eval(value);
		}
	}

    /**
     * 针对已查询出来的记录集，转换为 JavaScript 的 JSON 结构。
     * @param 	{RecroedSet} rs ADO 对象集合。
     * @return 	{Array}			输出的 JSON 数组。
     */
	function row2json(rs){
        var 
    	 output = [] 						// Array	输出的 JSON 数组
    	,rawArr	= rs.getRows().toArray()	// Array	getRows() 返回的数组
    	,fields = rs.Fields					// comObj	rs.Fields 字段对象，每一列都不同。
    	,fLen 	= fields.Count				// int		字段总数
		,len  	= rawArr.length / fLen		// int		记录数
		,sp									// int		位数
    	,row;								// object	构成 JSON 的行对象
		
		// 已经读取了数据，则自动关闭 RecordSet 集合，可以尽快释放 RecordSet 对象占用的资源。
    	rs.Close(); 
			
		for (var i = 0; i < len; i++){
			 sp	 = i * fLen
			,row = {};
			
			for (var j = 0; j < fLen; j++){
				row[fields(j).name] = sql2json(rawArr[sp + j], fields(j).Type);
			}
			output.push(row);
		}

        return output;
    }
};

$$.sql.INITIALIZE	= '\x100';
$$.sql.CREATE 		= '\x101';
$$.sql.READ 		= '\x102'
$$.sql.UPDATE 		= '\x103'
$$.sql.DELETE 		= '\x104'
$$.sql.OPTION		= '\x105';

/**
 * @class Edk.sql.DAO
 * DAO = Data Access Object 数据访问对象，既可读又可写的。
 */
$$.sql.DAO = function(){
	this.DAO = {
		 data		: null
		,instance	: this
	}

	$$.sql.Parser.apply(this.DAO, arguments);
//	$$.sql.Pagination.apply(this, arguments);
	
	/**
	 * @property {String} dbPrefix
	 * 在数据库中所有表名的前缀。
	 */
	var dbPrefix = 'edk_';
	
	/**
	 * @return {String}
	 */
	this.getTableName = function(){
		return dbPrefix + this.toString();
	}
	
	/**
	 * 输入一个id，返回其实体。
	 * @param  {Number} id
	 * @return {Entity}
	 */
	this.findById = function(id){
		var 
		 dao = this.DAO	// Object
		,sql			// String
		,json;			// JSON
		 
		 dao.setField(this.tableName  + this.getFields())
		,dao.setFilter('news.id=' + Request.QueryString('id')());
		
		sql		= dao.read();
		json	= dao.execute(sql);
		json	= json[0];
		
		return this.setValueByJson(json);
	}
	
	/**
	 * 默认执行方法的名称。
	 */
	this.defaultReadMethod = 'getDataWith_Catalog_User_Comment';
	
	/**
	 * 返回分页的数据部分，另外页面部分由模板提供。
	 * @param  {Number} page
	 * @return {Array}
	 */
	this.findPage = function(page){
		(this[this.defaultReadMethod])();
		
		this.getCatalog(true);
		this.getComment(true);
		
		// make SQL
		var allFields = [];	// 收集查询字段
		this.updateField(function(name, fieldObj, meta, _self){
			var field = fieldObj.getSql().alias;
			allFields.push(field);
		});
		
		allFields.reverse();
		allFields = allFields.join(', ');
		
		var dao = this.DAO;
		dao.setMainTable(this.getTableName());
		dao.setField(allFields);
	
		var sql = dao.getQuerySql();
		Response.write(sql);
//		debugger;
//		return [dao.execute(page.sql), page];
	}
	
	/**
	 * 分段查找
	 */
	this.findAll = function(){
		
	}
	
	this.findByLike = function(){
		
	}
	
	this.findBy = function (fn){
	}
	
	this.create = function(){}
	
	this.update = function(){}

	this.fill = function(){}
	
	this.del = function(){}
}